package com.github.aloxc.plugin.restplus.entity;

import com.github.aloxc.plugin.restplus.common.Consts;
import com.github.aloxc.plugin.restplus.method.HttpMethod;
import com.github.aloxc.plugin.restplus.utils.JsonUtils;
import com.github.aloxc.plugin.DonotGuard;
import com.github.aloxc.plugin.restplus.utils.Log;
import com.github.aloxc.plugin.restplus.navigator.RestServicesNavigator;
import com.github.aloxc.plugin.restplus.utils.ProjectUtil;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.registry.Registry;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * RestPlus
 *
 * @author liyh
 */
@DonotGuard
public class RestConfig implements Serializable {
    private static final long serialVersionUID = -4409316467501719038L;
    /**
     * 全局设置
     */
    @DonotGuard
    private GlobalConfig globalConfig;

    /**
     * 环境设置 key:环境名称 value：各环境配置
     */
    @DonotGuard
    private Map<String, EnvironmentConfig> env;


    @DonotGuard
    private static final String FILE_NAME = "/Restplus.json";
    /**
     * key：url
     * value：  key：caseName value：caseSetting
     */
    @DonotGuard
    private Map<String, Map<String, UserCase>> userCaseMap;

    /**
     * 请求设置
     */
    @DonotGuard
    private Map<String, RequestConfig> requestConfig;

    @DonotGuard
    private List<SwaggerConfig> swaggerConfigList;

    @DonotGuard
    private List<Yapi> yapiList;


    public List<SwaggerConfig> getSwaggerConfigList() {
        return swaggerConfigList;
    }

    public void setSwaggerConfigList(List<SwaggerConfig> swaggerConfigList) {
        this.swaggerConfigList = swaggerConfigList;
    }

    /**
     * 请求用例
     *
     * @return
     */
    public Map<String, Map<String, UserCase>> getUserCaseMap() {
        return userCaseMap;
    }

    public void setUserCaseMap(Map<String, Map<String, UserCase>> userCaseMap) {
        this.userCaseMap = userCaseMap;
    }

    public static RestConfig loadConfig(Project project) {
        try {
//p.getComponent(RestServicesNavigator.class)
//            Project project = project.getComponent(RestServicesNavigator.class).getProject();
//            Log.log("RestConfig","加载配置文件\t" + project.getBasePath());
            if (project == null) {
                return null;
            }
            int path = Registry.intValue(Consts.CONFIG_PATH);
            String configFile = null;
            if(path == 1){
                configFile = ProjectUtil.getConfigStorePath(project) + FILE_NAME;
            }else{
                configFile = project.getComponent(RestServicesNavigator.class).getProject().getBasePath() + FILE_NAME;
            }
            File file = new File(configFile);
            RestConfig config = null;
            if (!file.exists()) {
                config = new RestConfig();
                Vector<Setting> settingList = new Vector<>();
                settingList.add(new Setting("Cache-Control", "no-cache", Consts.VariateType.HEADER.getType(), true));
                settingList.add(new Setting("User-Agent", "RestAgent", Consts.VariateType.HEADER.getType(), true));
                settingList.add(new Setting("Accept", "*/*", Consts.VariateType.HEADER.getType(), true));
                settingList.add(new Setting("Accept-Encoding", "gzip, deflate, br", Consts.VariateType.HEADER.getType(), true));
                settingList.add(new Setting("Connection", "keep-alive", Consts.VariateType.HEADER.getType(), true));
                settingList.add(new Setting("Content-Type", "application/json", Consts.VariateType.HEADER.getType(), true));
                config.setGlobalConfig(new GlobalConfig(settingList, ""));
                Map<String, EnvironmentConfig> env = new HashMap(2 << 3);
                EnvironmentConfig envConfig = new EnvironmentConfig();
                envConfig.setSelected(true);
                envConfig.setHost("localhost");
                envConfig.setPort(8080);
                envConfig.setHttps(false);
                envConfig.setName("Default");
                envConfig.setTest("");
                envConfig.setSettingList(new Vector<>());
                env.put(envConfig.getName(), envConfig);
                config.setSwaggerConfigList(new ArrayList<>());
                config.setEnv(env);
                config.setUserCaseMap(new HashMap(2 << 3));
                config.setRequestConfig(new HashMap(2 << 3));
                config.setYapiList(new ArrayList<>());
                FileUtils.write(file, JsonUtils.toJson(config,true), "utf-8");
            } else {
                String content = FileUtils.readFileToString(file, "utf-8");
//                Log.log("RestConfig",content);
                try {
                    config = fromJson(content);
                }catch (Throwable throwable){
                    file.delete();
                    loadConfig(project);
                }
            }
            return config;
        } catch (IOException ioe) {
            Messages.showMessageDialog("Configuration file has error!", "Exception", Consts.Icons.EXCEPTION);
            System.exit(1);
        }
        return null;
    }

    public void save(Project project) {
        try {
            int path = Registry.intValue(Consts.CONFIG_PATH);
            String configFile = null;
            if(path == 1){
                configFile = ProjectUtil.getConfigStorePath(project) + FILE_NAME;
            }else{
                configFile = project.getComponent(RestServicesNavigator.class).getProject().getBasePath() + FILE_NAME;
            }
            File file = new File(configFile);
            FileUtils.write(file, JsonUtils.toJson(this,true), "utf-8");
        } catch (IOException ioe) {
            Messages.showMessageDialog("Configuration file has error!", "Exception", Consts.Icons.EXCEPTION);
        }
    }


    public Map<String, EnvironmentConfig> getEnv() {
        return env;
    }

    public void setEnv(Map<String, EnvironmentConfig> env) {
        this.env = env;
    }

    public RestConfig() {
//        this.init();
    }


    public GlobalConfig getGlobalConfig() {
        return globalConfig;
    }

    public void setGlobalConfig(GlobalConfig globalConfig) {
        this.globalConfig = globalConfig;
    }

    public Map<String, RequestConfig> getRequestConfig() {
        return requestConfig;
    }

    public void setRequestConfig(Map<String, RequestConfig> requestConfig) {
        this.requestConfig = requestConfig;
    }

    /**
     * 为什么要这样的狗屎写法：由于使用jackson也好，gson也好都可能把bean解析成linkedhashmap，导致异常
     *
     * @param json
     * @return
     */
    private static RestConfig fromJson(String json) throws Throwable {
        RestConfig config = new RestConfig();
        Map _root = JsonUtils.toBean(json, HashMap.class);
        GlobalConfig globalConfig = new GlobalConfig();
        HashMap _globalConfig = (HashMap) _root.get("globalConfig");
        String _test = (String) _globalConfig.get("test");
        List _settingList = (List) _globalConfig.get("settingList");
        List _yapiList = (List)_root.get("yapiList");
        List<Setting> settingList = new ArrayList<>();
        for (Object o : _settingList) {
            HashMap _setting = (HashMap) o;
            Setting setting = new Setting();
            String _name = (String) _setting.get("name");
            String _value = (String) _setting.get("value");
            String _type = (String) _setting.get("type");
            boolean _selected = (boolean) _setting.get("selected");
            setting.setName(_name);
            setting.setValue(_value);
            setting.setType(_type);
            setting.setSelected(_selected);
            settingList.add(setting);
        }

        globalConfig.setTest(_test);
        globalConfig.setSettingList(settingList);
        config.setGlobalConfig(globalConfig);

        Map<String, RequestConfig> requestConfigMap = new HashMap<>(16);
        HashMap _requestMap = (HashMap) _root.get("requestConfig");
        for (Object k : _requestMap.keySet()) {
            String _key = (String) k;
            RequestConfig requestConfig = new RequestConfig();
            HashMap _requestConfig = (HashMap) _requestMap.get(k);
            _test = (String) _requestConfig.get("test");
            _settingList = (List) _requestConfig.get("settingList");
            requestConfig.setTest(_test);
            settingList = new ArrayList<>();
            for (Object o : _settingList) {
                HashMap _setting = (HashMap) o;
                Setting setting = new Setting();
                String _name = (String) _setting.get("name");
                String _value = (String) _setting.get("value");
                String _type = (String) _setting.get("type");
                boolean _selected = (boolean) _setting.get("selected");
                setting.setSelected(_selected);
                setting.setType(_type);
                setting.setValue(_value);
                setting.setName(_name);
                settingList.add(setting);
            }
            requestConfig.setSettingList(settingList);
            requestConfigMap.put(_key, requestConfig);
        }
        config.setRequestConfig(requestConfigMap);

        HashMap _envMap = (HashMap) _root.get("env");
        Map<String, EnvironmentConfig> environmentConfigMap = new HashMap<>(16);
        for (Object k : _envMap.keySet()) {
            String _name = (String) k;
            EnvironmentConfig environmentConfig = new EnvironmentConfig();
            HashMap _environmentConfig = (HashMap) _envMap.get(k);
            boolean _selected = (boolean) _environmentConfig.get("selected");
            String _context = (String) _environmentConfig.get("context");
            _test = (String) _environmentConfig.get("test");
            boolean _https = (boolean) _environmentConfig.get("https");
            String _host = (String) _environmentConfig.get("host");
            int _port = (int) _environmentConfig.get("port");
            _settingList = (List) _environmentConfig.get("settingList");
            Vector<Setting> settingList1 = new Vector<>();
            for (Object o : _settingList) {
                HashMap _setting = (HashMap) o;
                String _sname = (String) _setting.get("name");
                String _value = (String) _setting.get("value");
                String _type = (String) _setting.get("type");
                boolean _sselected = (boolean) _setting.get("selected");
                Setting setting = new Setting();
                setting.setName(_name);
                setting.setValue(_value);
                setting.setType(_type);
                setting.setSelected(_sselected);
                settingList1.add(setting);
            }
            environmentConfig.setName(_name);
            environmentConfig.setSelected(_selected);
            environmentConfig.setHost(_host);
            environmentConfig.setHttps(_https);
            environmentConfig.setPort(_port);
            environmentConfig.setTest(_test);
            environmentConfig.setContext(_context);
            environmentConfig.setSettingList(settingList1);
            environmentConfigMap.put(_name, environmentConfig);
        }
        config.setEnv(environmentConfigMap);

        Map<String, Map<String, UserCase>> userCaseMap = new HashMap<>(16);
        HashMap _userCaseMap = (HashMap) _root.get("userCaseMap");
        for (Object k : _userCaseMap.keySet()) {
            String _url = (String) k;
            HashMap _userCasem = (HashMap) _userCaseMap.get(k);
            Map<String, UserCase> up = new HashMap<>(16);
            for (Object uk : _userCasem.keySet()) {
                String _name = (String) uk;
                HashMap _userCase = (HashMap) _userCasem.get(_name);
                UserCase userCase = new UserCase();
                _test = (String) _userCase.get("test");
                HashMap<String, Object> _headerMap = (HashMap<String, Object>) _userCase.get("headerMap");
                HashMap<String, Object> _paramMap = (HashMap<String, Object>) _userCase.get("paramMap");
                HashMap<String, Object> _bodyMap = (HashMap<String, Object>) _userCase.get("bodyMap");
                userCase.setTest(_test);
                userCase.setBodyMap(_bodyMap);
                userCase.setHeaderMap(_headerMap);
                userCase.setParamMap(_paramMap);
                up.put(_name, userCase);
            }
            userCaseMap.put(_url, up);
        }

        config.setUserCaseMap(userCaseMap);
        List<HashMap> _swaggerConfigList = (List<HashMap>) _root.get("swaggerConfigList");
        config.setSwaggerConfigList(new ArrayList<>());
        if(_swaggerConfigList == null){
            return config;
        }
        List<SwaggerConfig> swaggerConfigList = config.getSwaggerConfigList();

        _swaggerConfigList.stream().forEach(_swaggerConfig ->{
            String name = (String) _swaggerConfig.get("name");
            List<HashMap> _swaggerRequestList = (List<HashMap>) _swaggerConfig.get("swaggerRequestList");
            List<SwaggerRequest> swaggerRequestList = new ArrayList<>();
            _swaggerRequestList.stream().forEach(_swaggerRequest ->{
                String url = (String) _swaggerRequest.get("url");
                String method = (String) _swaggerRequest.get("method");
                String name1 = (String) _swaggerRequest.get("name");
                String param = (String) _swaggerRequest.get("param");
                String description = (String) _swaggerRequest.get("description");
                SwaggerRequest swaggerRequest = new SwaggerRequest();
                swaggerRequest.setUrl(url);
                swaggerRequest.setName(name1);
                swaggerRequest.setParam(param);
                swaggerRequest.setDescription(description);
                swaggerRequest.setMethod(HttpMethod.getByRequestMethod(method));
                swaggerRequestList.add(swaggerRequest);
            });
            SwaggerConfig swaggerConfig = new SwaggerConfig();
            swaggerConfig.setName(name);
            swaggerConfig.setSwaggerRequestList(swaggerRequestList);
            swaggerConfigList.add(swaggerConfig);
        });

        List<Yapi> yapiList = new ArrayList<>();
        for (Object o : _yapiList) {
            HashMap _yapi = (HashMap) o;
            Yapi yapi = new Yapi();
            String _module = (String) _yapi.get("module");
            String _projectId = (String) _yapi.get("projectId");
            String _token = (String) _yapi.get("token");
            String _url = (String) _yapi.get("url");
            yapi.setProjectId(_projectId);
            yapi.setModule(_module);
            yapi.setUrl(_url);
            yapi.setToken(_token);
            yapiList.add(yapi);
        }
        config.setYapiList(yapiList);

//        config.setSwaggerConfigList(swaggerConfigList);
        return config;
    }

    public List<Yapi> getYapiList() {
        return yapiList;
    }

    public void setYapiList(List<Yapi> yapiList) {
        this.yapiList = yapiList;
    }

    public Yapi getYapi(Module module) {
        if(this.yapiList == null || yapiList.size() == 0){
            return null;
        }
        Map<String, Yapi> map = yapiList.stream().collect(Collectors.toMap(k -> k.getModule(), v -> v, (k1, k2) -> k1));
        return map.get(module.getName());
    }
}
